package com.julian.mvc.djview.model;

import com.julian.mvc.djview.view.BPMObserver;
import com.julian.mvc.djview.view.BeatObserver;

import javax.sound.midi.*;
import java.util.ArrayList;

public class BeatModel implements BeatModeInterface, MetaEventListener {
    Sequencer sequencer;//it knows how to create real beat that you listen
    ArrayList beatObservers = new ArrayList();
    ArrayList bpmObservers = new ArrayList();
    int bpm = 90;//default 90
    Sequence sequence;
    Track track;


    @Override
    public void initialize() {
        setUpMiDi();
        buildTrackAndStart();
    }

    @Override
    public void on() {
        sequencer.start();
    }

    @Override
    public void off() {
        setBPM(0);
        sequencer.stop();
    }

    @Override
    public void setBPM(int BPM) {
        this.bpm = BPM;
        sequencer.setTempoInBPM(getBPM());
        notifyBPMObservers();
    }

    @Override
    public int getBPM() {
        return bpm;
    }

    void beatEvent() {
        notifyBeatObservers();
    }
    @Override
    public void registerObserver(BeatObserver observer) {
        beatObservers.add(observer);
    }

    public void notifyBeatObservers(){
        for (int i = 0; i < beatObservers.size(); i++){
            BeatObserver observer = (BeatObserver) beatObservers.get(i);
            observer.updateBeat();
        }
    }

    @Override
    public void removeObserver(BeatObserver observer) {
        int i = beatObservers.indexOf(observer);
        if (i >= 0){
            beatObservers.remove(i);
        }
    }

    @Override
    public void registerObserver(BPMObserver observer) {
        bpmObservers.add(observer);
    }

    public void notifyBPMObservers(){
        for (int i = 0; i < bpmObservers.size(); i++){
            BPMObserver observer = (BPMObserver)bpmObservers.get(i);
            observer.updateBPM();
        }
    }

    @Override
    public void removeObserver(BPMObserver observer) {
        int i = bpmObservers.indexOf(observer);
        if (i >= 0){
            bpmObservers.remove(i);
        }
    }

    @Override
    public void meta(MetaMessage meta) {
        if (meta.getType() == 47){
            beatEvent();
            sequencer.start();
            setBPM(getBPM());
        }
    }

    public void setUpMiDi(){
        try{
            sequencer = MidiSystem.getSequencer();
            sequencer.open();
            sequencer.addMetaEventListener(this);
            sequence = new Sequence(Sequence.PPQ,4);
            track = sequence.createTrack();
            sequencer.setTempoInBPM(getBPM());
        }catch (Exception e){
            e.printStackTrace();
        }
    }

    public void buildTrackAndStart(){
        int[] trackList = {35, 0, 46, 0};

        sequence.deleteTrack(null);
        track = sequence.createTrack();

        makeTracks(trackList);
        track.add(makeEvent(192,9,1,0,4));
        try{
            sequencer.setSequence(sequence);
        }catch (Exception e){
            e.printStackTrace();
        }
    }

    public void makeTracks(int[] list){
        for (int i = 0; i < list.length; i++){
            int key = list[i];

            if (key != 0){
                track.add(makeEvent(144,9,key,100,i));
                track.add(makeEvent(128,9,key,100,i+1));
            }
        }
    }
    public MidiEvent makeEvent(int comd, int chan, int one, int two, int tick){
        MidiEvent event = null;
        try{
            ShortMessage shortMessage = new ShortMessage();
            shortMessage.setMessage(comd,chan,one,two);
            event = new MidiEvent(shortMessage,tick);
        }catch (Exception e){
            e.printStackTrace();
        }
        return event;
    }
}
